package com.fs.service;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fs.auth.TokenGenerator;
import com.fs.mapper.*;
import com.fs.pojo.*;
import com.fs.vo.ForgetVO;
import com.fs.vo.LoginVO;
import com.fs.vo.RegFormVO;
import com.fs.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{

    @Value("${zyz.user.login-times:3}")
    private Integer userLoginTimes;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private EmailCaptService emailCaptService;

    @Autowired
    private CaptMapper captMapper;

    @Autowired
    private UserTokenMapper userTokenMapper;
    @Transactional
    @Override
    public ResultVO reg(RegFormVO formVO) {
        if (!formVO.getAccept()) {
            return ResultVO.fail("请先同意《用户协议》");
        }

        Date now = new Date();
        String username = formVO.getUsername();
        String password = formVO.getPassword();
        String email = formVO.getEmail();
        String code = formVO.getCode();
        User user = null;

        //查找邮件验证码是否还在有效期
        QueryWrapper<EmailCapt> wrapper = new QueryWrapper();
        wrapper.eq("email", email)
                .eq("status", false)
                .eq("type",1)
                .gt("expire_time",now)
                .orderByDesc("expire_time");
        EmailCapt one1 = emailCaptService.getOne(wrapper);
        if(one1 == null){
            return ResultVO.fail("邮件验证码已过期，请重新发送");
        }
        // 验证码是否正确
        if(!one1.getCode().equals(code)){
            return ResultVO.fail("邮件验证码不正确");
        }

        one1.setStatus(true);
        emailCaptService.update(one1,wrapper);

        //检测用户名
        if (username == null) {
            return ResultVO.fail("请填写用户名");
        }

        //判断用户名是否符合规则
        String usernameRegex = "^\\w{5,16}$";
        if (!username.matches(usernameRegex)) return ResultVO.fail("用户名5-16位，字母或数字");

        //判断密码是否符合规则
        String passwordRegex = "^\\w{5,16}$";
        if (!password.matches(passwordRegex)) return ResultVO.fail("密码5-16位，字母或数字");

        //检测邮箱格式
        String emailRegex = "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}";
        if (!email.matches(emailRegex)) return ResultVO.fail("邮箱格式不正确");

        //检测用户名是否重复
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper.eq("username", username);
        user = userMapper.selectOne(queryWrapper);
        if (user != null) {
            return ResultVO.fail("账户重复");
        }

        //检测邮箱是否已使用
        queryWrapper.clear();
        queryWrapper.eq("email",email);
        user = userMapper.selectOne(queryWrapper);
        if (user != null) {
            return ResultVO.fail("邮箱已被注册，请更换邮箱");
        }

        String salt = RandomUtil.randomString(8);
        String md5 = SecureUtil.md5(password + salt);

        user=new User();
        user.setUsername(username)
                .setEmail(email)
                .setPhone(null)
                .setSalt(salt)
                .setPassword(md5)
                .setId(null)
                .setBalance(0)
                .setScore(0)
                .setStatus(true)
                .setCreated(now)
                .setUpdated(now);
        int i = userMapper.insert(user);

        //更新附表
        UserInfo userInfo = new UserInfo();
        userInfo.setSex(null)
                .setBirthday(null)
                .setAddr(null)
                .setUserId(user.getId());
        int i1 = userInfoMapper.insert(userInfo);

        if (i > 0 && i1 > 0) {
            return ResultVO.success();
        }
        return ResultVO.fail("用户添加失败");
    }

    @Transactional
    @Override
    public ResultVO forget(ForgetVO vo) {

        Date now = new Date();
        String email = vo.getUsername();
        String password = vo.getPassword();
        String password2 = vo.getPassword2();
        String code = vo.getCode();

        if(!password.equals(password2)){
            return ResultVO.fail("密码不一致");
        }

        //判断密码是否符合规则
        String passwordRegex = "^\\w{5,16}$";
        if (!password.matches(passwordRegex)) return ResultVO.fail("密码5-16位，字母或数字");

        //检测邮箱格式
        String emailRegex = "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}";
        if (!email.matches(emailRegex)) return ResultVO.fail("邮箱格式不正确");

        //查找邮件验证码是否还在有效期
        QueryWrapper<EmailCapt> wrapper = new QueryWrapper();
        wrapper.eq("email", email)
                .eq("status", false)
                .eq("type",2)
                .gt("expire_time",now)
                .orderByDesc("expire_time");
        EmailCapt one1 = emailCaptService.getOne(wrapper);
        if(one1 == null){
            return ResultVO.fail("邮件验证码已过期，请重新发送");
        }
        // 验证码是否正确
        if(!one1.getCode().equals(code)){
            return ResultVO.fail("邮件验证码不正确");
        }

        //检测用户名是否重复
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper.eq("email", email);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            return ResultVO.fail("邮箱未注册");
        }

        one1.setStatus(true);
        emailCaptService.update(one1,wrapper);

        String md5 = SecureUtil.md5(password + user.getSalt());

        user
                .setPassword(md5)
                .setUpdated(now);
        this.updateById(user);

        return ResultVO.success();
    }

    @Override
    public ResultVO login(LoginVO vo) {
        if(vo==null){
            return ResultVO.fail("登陆失败");
        }
        String username = vo.getUsername();
        String password = vo.getPassword();
        String caption = vo.getCode();
        String captionUUID = vo.getStr1();

        if (caption == null || captionUUID == null) {
            return ResultVO.fail("请输入验证码");
        }
        Capt captEntity = captMapper.selectById(captionUUID);
        if (captEntity == null) return ResultVO.fail("验证码失效");
        Date expireTime = captEntity.getExpireTime();
        Date nowTime = new Date();
        if (expireTime.compareTo(nowTime) < 0) return ResultVO.fail("验证码失效");
        if (!caption.equalsIgnoreCase(captEntity.getCode())) return ResultVO.fail("验证码错误");
        //删除验证码
        captMapper.deleteById(captionUUID);

        //判断是用户名还是邮箱
        String loginType="";
        int isHaveAt = username.indexOf("@");
        if(isHaveAt > 0){
            loginType="emailType";

            //判断用户名是否符合规则
            String emailRegex = "^\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}$";
            if (!username.matches(emailRegex)) return ResultVO.fail("邮箱格式有误");

        }else{
            loginType="usernameType";

            //判断用户名是否符合规则
            String usernameRegex = "^\\w{5,16}$";
            if (!username.matches(usernameRegex)) return ResultVO.fail("用户名5-16位，字母或数字");
        }


        //判断密码是否符合规则
        String passwordRegex = "^\\w{5,16}$";
        if (!password.matches(passwordRegex)) return ResultVO.fail("密码5-16位，字母或数字");


        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper
                .eq("usernameType".equals(loginType),"username", username)
                .eq("emailType".equals(loginType),"email", username)
        ;
        userMapper.selectObjs(queryWrapper);
        List<User> adminEntityList = userMapper.selectList(queryWrapper);
        if (adminEntityList.size() == 0) {
            return ResultVO.fail("账户不存在");
        } else if (adminEntityList.size() > 1) {
            return ResultVO.fail("账户重复");
        }
        User user = adminEntityList.get(0);

        // 登陆次数是否超过限制
        Integer haveErrNum = user.getErrNum()!=null ?user.getErrNum():0;
        if (haveErrNum >= userLoginTimes) {
            return ResultVO.fail("登陆次数达到" + userLoginTimes + "次，账户已禁用");
        }


        //判断密码
        String md5 = SecureUtil.md5(password + user.getSalt());
        log.debug("登陆密码：{}",md5);
        if (!md5.equalsIgnoreCase(user.getPassword())) {
            //记录登陆失败次数、日期
            user.setErrNum(haveErrNum + 1);
            user.setErrTime(new Date());
            if (haveErrNum + 1 == userLoginTimes) {
                user.setStatus(false);
                user.setErrTime(new Date());
                userMapper.updateById(user);
                return ResultVO.fail("登陆次数达到" + userLoginTimes + "次，账户已禁用");
            }
            userMapper.updateById(user);
            return ResultVO.fail("登陆失败");
        }
        //判断状态
        if (!user.getStatus()) {
            return ResultVO.fail("账户已禁用");
        }
        //登陆错误次数清零
        user.setErrNum(0);
        user.setErrTime(null);
        userMapper.updateById(user);

        //生成token
        Map<String, Object> result = new HashMap();
        result.put("token", TokenGenerator.generateValue());
        result.put("username", user.getUsername());
        result.put("userId", user.getId());
        result.put("status", true);
        Calendar calendar = new GregorianCalendar();
        calendar.add(calendar.DATE, 1);
        result.put("expire_time", calendar.getTime());

        UserToken token = new UserToken();
        token.setUserId(user.getId());
        token.setToken(result.get("token").toString());
        token.setEndTime((Date) result.get("expire_time"));
        token.setUpdated(new Date());
        if (userTokenMapper.selectById(token) == null) {
            userTokenMapper.insert(token);
        } else {
            userTokenMapper.updateById(token);
        }
        return ResultVO.success(result);
    }

    @Transactional
    @Override
    public ResultVO pwd(Map<String, String> map,User user) {
        String p1=map.get("pw1");
        String p2=map.get("pw2");
        String p3=map.get("pw3");

        //判断密码是否符合规则
        String passwordRegex = "^\\w{5,16}$";
        if (!p1.matches(passwordRegex)) return ResultVO.fail("密码5-16位，字母或数字");
        if (!p2.matches(passwordRegex)) return ResultVO.fail("密码5-16位，字母或数字");

        if(!p2.equalsIgnoreCase(p3)){
            return ResultVO.fail("密码不一致");
        }

        String s = SecureUtil.md5(p1 + user.getSalt());
        if(!user.getPassword().equalsIgnoreCase(s)){
            return ResultVO.fail("旧密码不正确");
        }

        String newPwd = SecureUtil.md5(p2 + user.getSalt());
        user.setPassword(newPwd);
        userMapper.updateById(user);

        userMapper.deleteToken(user.getId());

        return ResultVO.success();
    }

}
